package com.dayuanit.dy16.atm.service.impl;

import com.dayuanit.dy16.atm.dto.CardDTO;
import com.dayuanit.dy16.atm.entity.Card;
import com.dayuanit.dy16.atm.entity.Flow;
import com.dayuanit.dy16.atm.entity.TransferOrder;
import com.dayuanit.dy16.atm.enums.FlowTypeEnum;
import com.dayuanit.dy16.atm.enums.TransferOrderStatusEnums;
import com.dayuanit.dy16.atm.exception.BizException;
import com.dayuanit.dy16.atm.mapper.CardMapper;
import com.dayuanit.dy16.atm.mapper.FlowMapper;
import com.dayuanit.dy16.atm.mapper.TransferOrderMapper;
import com.dayuanit.dy16.atm.service.ICardService;
import com.dayuanit.dy16.atm.service.IRedisService;
import com.dayuanit.dy16.atm.util.CardUtils;
import com.dayuanit.dy16.atm.vo.TransferRequestVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Service
public class CardServiceImpl implements ICardService {

    @Autowired
    private CardMapper cardMapper;

    @Autowired
    private FlowMapper flowMapper;

    @Autowired
    private TransferOrderMapper transferOrderMapper;

    @Resource(name = "redisService4SpringImpl")
    private IRedisService redisService;

    @Override
    public void openaccount(String password, String confirmPwd, long userId) {
        if (StringUtils.isBlank(password) || StringUtils.isBlank(confirmPwd)) {
            throw new BizException("有必填参数为空");
        }

        if (password.length() < 3 || password.length() > 20) {
            throw new BizException("密码长度3-20");
        }

        if (!password.equals(confirmPwd)) {
            throw new BizException("两次密码不相等");
        }

        String cardNum = null;
        Card card = null;
        do {
            cardNum = CardUtils.createCardNum();
            card = cardMapper.getByCardNum(cardNum);
        } while (null != card);

        card = new Card();
        card.setBalance(0);
        card.setCardNum(cardNum);
        card.setCreateTime(new Date());
        card.setModifyTime(card.getCreateTime());
        card.setPwd(password);
        card.setUserId(userId);

        cardMapper.insert(card);
    }

    /**
     * 在写service层代码时，一定要注意以下两个问题：
     *  1.事务
     *  2.并发
     * @param cardId
     * @param password
     * @param amount
     * @param userId
     */
    @Transactional
    @Override
    public void deposit(long cardId, String password, String amount, long userId) {
        if (StringUtils.isBlank(password) || StringUtils.isBlank(amount)) {
            throw new BizException("有必填参数为空");
        }

        //数据库的分布式锁  又称之为“悲观锁” 就是数据库中的“行锁”
        // 悲观锁 在高并发情况下 能够保证准确率
        Card card = cardMapper.get4Lock(cardId);
        if (null == card) {
            throw new BizException("银行卡不存在或密码错误");
        }

        if (card.getUserId() != userId) {
            throw new BizException("你无权操作");
        }

        if (!password.equals(card.getPwd())) {
            throw new BizException("银行卡不存在或密码错误");
        }

        Integer balance = card.getBalance();
        int newBalance = (int)(Double.parseDouble(amount) * 100) + card.getBalance();

        //使用乐观锁 更新数据
        int rows = cardMapper.updateBalance(cardId, newBalance, card.getBalance());
        if (1 != rows) {
            throw new BizException("存款失败");
        }

        // 生成一笔流水
        createFlow(FlowTypeEnum.deposit, card, amount, userId);

        //TODO 将redis的key设置为失效
        //atm:flow:top10:
        redisService.expireKey("atm:flow:top10:" + userId, 0);
    }

    @Transactional
    @Override
    public void tranfer(TransferRequestVO transferRequestVO, long userId) {
        if (StringUtils.isBlank(transferRequestVO.getAmount())
                || StringUtils.isBlank(transferRequestVO.getCardNumberIn())
                || StringUtils.isBlank(transferRequestVO.getPassword())) {
            throw new BizException("有必填参数为空");
        }

        // 查询银行卡
        Card cardIn = cardMapper.getByCardNum(transferRequestVO.getCardNumberIn());
        Card cardOut = cardMapper.selectByPrimaryKey(transferRequestVO.getCardOutId());

        if (cardIn == null || cardOut == null) {
            throw new BizException("银行卡号错误或者密码错误");
        }

        // 校验密码
        if (!cardOut.getPwd().equals(transferRequestVO.getPassword())) {
            throw new BizException("银行卡号错误或者密码错误");
        }

        // 余额检查
        double amount = Double.parseDouble(transferRequestVO.getAmount());
        if (amount > cardOut.getBalance()) {
            throw new BizException("余额不足请充值");
        }

        // 转出余额修改+流水
        int newBalance = cardOut.getBalance() - (int)(amount * 100);
        int rows = cardMapper.updateBalance(cardOut.getId(), newBalance, cardOut.getBalance());
        if (1 != rows) {
            throw new BizException("转账失败");
        }

       createFlow(FlowTypeEnum.tranfer_out, cardOut, transferRequestVO.getAmount(), userId);

        // 转入余额修改+流水
        newBalance = cardIn.getBalance() + (int)(amount * 100);
        rows = cardMapper.updateBalance(cardIn.getId(), newBalance, cardIn.getBalance());
        if (1 != rows) {
            throw new BizException("转账失败");
        }

        createFlow(FlowTypeEnum.tranfer_in, cardIn, transferRequestVO.getAmount(), userId);

        redisService.expireKey("atm:flow:top10:" + userId, 0);
    }


    private void createFlow(FlowTypeEnum flowTypeEnum, Card card, String amount, long userId) {
        Flow flow = new Flow();
        flow.setUserId(userId);
        flow.setRemark(flowTypeEnum.getV());
        flow.setFlowType(flowTypeEnum.getK());
        flow.setCreateTime(new Date());
        flow.setCardId(card.getId());
        flow.setAmount(amount);
        flowMapper.insert(flow);
    }

    @Override
    public List<CardDTO> queryMyCard(long userId) {
        List<Card> cards = cardMapper.listByUserId(userId);
        List<CardDTO> dtos = new ArrayList(cards.size());

        for (Card card : cards) {
            CardDTO cardDTO = new CardDTO();
            dtos.add(cardDTO);

            cardDTO.setBalance(String.valueOf(card.getBalance()/ 100));
            cardDTO.setCardId(card.getId());
            cardDTO.setCardNumber(CardUtils.markCardNum(card.getCardNum()));
        }
        return dtos;
    }

    /**
     * 乐观锁
     * @param cardId
     * @param password
     * @param amount
     * @param userId
     */
    //amount = -3.90
    @Transactional
    @Override
    public void draw(long cardId, String password, String amount, long userId) {
        if (StringUtils.isBlank(password) || StringUtils.isBlank(amount)) {
            throw new BizException("有必填参数为空");
        }

        Card card = cardMapper.selectByPrimaryKey(cardId);
        if (null == card || !card.getPwd().equals(password)) {
            throw new BizException("银行卡不存在或者密码错误");
        }

        //判断余额
        int amountNumber = (int)(Double.parseDouble(amount) * 100);
        if (amountNumber <= 0) {
            throw new BizException("非法金额");
        }

        if (card.getBalance() < amountNumber) {
            throw new BizException("余额不足请充值");
        }

        int newBalance = card.getBalance() - amountNumber;

        //乐观锁 新旧两个值比较  并发高的情况下 失败率比较高
        int rows = cardMapper.updateBalance(cardId, newBalance, card.getBalance());
        if (1 != rows) {
            throw new BizException("取款失败");
        }

        // 生成流水
        createFlow(FlowTypeEnum.draw, card, amount, userId);

        redisService.expireKey("atm:flow:top10:" + userId, 0);
    }

    /**
     * 第一步 转账扣钱
     * @param transferRequestVO
     * @param userId
     */
    @Transactional
    @Override
    public void delayTransfer(TransferRequestVO transferRequestVO, long userId) {
        if (StringUtils.isBlank(transferRequestVO.getAmount())
                || StringUtils.isBlank(transferRequestVO.getCardNumberIn())
                || StringUtils.isBlank(transferRequestVO.getPassword())) {
            throw new BizException("有必填参数为空");
        }

        int amountInt = (int)(Double.parseDouble(transferRequestVO.getAmount()) * 100);
        if (amountInt <= 0) {
            throw new BizException("金额不正确");
        }

        Card cardIn = cardMapper.getByCardNum(transferRequestVO.getCardNumberIn());
        if (null == cardIn) {
            throw new BizException("转入银行卡不存在");
        }

        //悲观锁
        Card cardOut = cardMapper.get4Lock(transferRequestVO.getCardOutId());
        if (cardOut.getBalance() < amountInt) {
            throw new BizException("余额不足请充值");
        }

        int newBalance = cardOut.getBalance() - amountInt;
        int rows = cardMapper.updateBalance(cardOut.getId(), newBalance, cardOut.getBalance());
        if (1 != rows) {
            throw new BizException("转账失败");
        }

        //添加流水
        createFlow(FlowTypeEnum.tranfer_out, cardOut, transferRequestVO.getAmount(), userId);

        // 生成一笔转账订单
        TransferOrder transferOrder = new TransferOrder();
        transferOrder.setAmount(transferRequestVO.getAmount());
        transferOrder.setCardInId(cardIn.getId());
        transferOrder.setUserId(userId);
        transferOrder.setCreateTime(new Date());
        transferOrder.setStatus(TransferOrderStatusEnums.wait_transfer.getK());
        transferOrder.setCardOutId(cardOut.getId());
        transferOrderMapper.insert(transferOrder);

        redisService.expireKey("atm:flow:top10:" + userId, 0);
    }
}
